package cmd

import (
	"colly/models"
	"colly/services"
	"colly/types"
	"context"
	"encoding/json"
	"github.com/gocolly/colly/v2"
	"github.com/gocolly/colly/v2/extensions"
	"github.com/hibiken/asynq"
	"github.com/panjf2000/ants/v2"
	"github.com/spf13/cobra"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"
)

func init() {
	runDir, _ = getExecutableDir()
	goldCmd.PersistentFlags().StringVarP(&proxyFile, "proxy", "p", runDir+"/clash.yaml", "clash file path")
	goldCmd.PersistentFlags().StringVarP(&serverName, "name", "n", "", "server path name")
	goldCmd.PersistentFlags().StringVarP(&currency, "currency", "C", "USD", "server path name")
	goldCmd.PersistentFlags().IntVarP(&id, "id", "i", 0, "pk id")
	rootCmd.AddCommand(goldCmd)
}

var goldCmd = &cobra.Command{
	Use:   "gold",
	Short: "This is the gold command",
	Long:  "This is the gold command",
	PreRun: func(cmd *cobra.Command, args []string) {
		if proxyFile == "" {
			proxyFile = "./clash.yaml"
		}
	},
	Run: func(cmd *cobra.Command, args []string) {
		queue := NewRedisClient()
		defer queue.Close()

		if serverName != "" {
			Log.Warning("Not Support")
			//exec(queue, serverName)
			return
		}

		games := make([]models.Game, 0)
		Db.Where("enable = ?", 2).Find(&games)
		//Db.Where("id = ?", 83).Find(&games)
		if len(games) < 1 {
			Log.Info("No Record Found")
			time.Sleep(time.Second * 5)
			return
		}

		defer ants.Release()
		var wg sync.WaitGroup
		pool, _ := ants.NewPool(len(games) + 1)
		defer pool.Release()
		task := func(queue *asynq.Client, serverUrl, serverName string, page int, wg *sync.WaitGroup) func() {
			return func() {
				defer wg.Done()
				exec(queue, serverUrl, serverName, page)
			}
		}
		for _, game := range games {
			if id != 0 && game.ID != id {
				continue
			}
			Log.Infof("Game: %s", game.Name)
			wg.Add(1)
			serverUrl := game.URL
			serverName := game.Name
			pool.Submit(task(queue, serverUrl, serverName, 1, &wg))
			//Db.Table(game.TableName()).Where("id = ?", game.ID).Update("enable", 1)
		}
		wg.Wait()
	},
}

func exec(queue *asynq.Client, serverUrl, serverName string, page int) {
	url := "https://sls.g2g.com/offer/search?seo_term=" + serverUrl + "&sort=recommended&page_size=100&currency=" + currency + "&country=US&page=" + strconv.Itoa(page)
	c := colly.NewCollector(
		colly.MaxDepth(1),
		//colly.Debugger(&debug.LogDebugger{}),
	)
	extensions.RandomUserAgent(c)
	c.Limit(&colly.LimitRule{
		RandomDelay: 500 * time.Millisecond,
		Parallelism: 10,
	})
	cookie := &http.Cookie{
		Name:  "g2g_regional",
		Value: "%7B%22country%22%3A%22US%22%2C%22currency%22%3A%22" + currency + "%22%2C%22language%22%3A%22en%22%7D",
	}
	c.SetCookies(url, []*http.Cookie{cookie})
	c.OnRequest(func(request *colly.Request) {
		request.Headers.Set("Accept-Language", "en,zh-TW;q=0.9,zh;q=0.8")
	})
	//
	resp := services.RandomConn(proxyFile, url)
	defer resp.NetConn.Close()
	c.WithTransport(&http.Transport{
		DialContext: func(ctx context.Context, network, url string) (net.Conn, error) {
			return resp.NetConn, nil
		},
		MaxIdleConns:          100,
		IdleConnTimeout:       5 * time.Second,
		TLSHandshakeTimeout:   3 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	})

	Log.Infof("name: %s, url:%s, proxy=%s, url=%s\n", serverName, serverUrl, resp.Proxy.Name(), url)

	c.OnResponse(func(response *colly.Response) {
		//os.WriteFile("./temp/"+serverUrl+".json", response.Body, 0777)
		ret := &types.ServerResponse{}
		err := json.Unmarshal(response.Body, &ret)
		if err != nil {
			Log.Errorln("error: " + err.Error())
			return
		}

		fomart := "server=%s, id=%d, game=%s, server=%s, price=%s, currency=%s"
		query := "game = ? and collect = ? and title = ?  and dataset = ? "
		tm := time.Now()
		for _, item := range ret.Payload.Results {
			object := models.Gold{}
			Db.Where(query, serverName, item.OfferAttributes[0].CollectionID, item.Title, item.OfferAttributes[0].DatasetID).First(&object)
			price, _ := strconv.ParseFloat(item.FormattedUnitPrice, 64)
			//localPrice, _ := strconv.ParseFloat(item.DisplayPrice, 64)
			if object.ID < 1 {
				//object.ID = 1
				object.Enable = 0
				object.ServerID = 0
				object.Game = serverName
				object.Title = item.Title
				object.Sku = item.BrandID
				object.ServiceID = item.ServiceID
				object.Currency = item.OfferCurrency
				object.Collect = item.OfferAttributes[0].CollectionID
				object.Dataset = item.OfferAttributes[0].DatasetID
				object.Price = price
				object.OfferAttribute, _ = json.Marshal(item.OfferAttributes)
				object.CreatedAt = tm
				object.UpdatedAt = tm
				object.CreateTime = tm.Unix()
				object.UpdateTime = tm.Unix()
				Db.Create(&object)
			}

			// 关联服务
			if object.ID > 0 {
				updates := make(map[string]interface{})
				updates["currency"] = item.OfferCurrency
				updates["price"] = price
				updates["updated_at"] = tm
				updates["update_time"] = tm.Unix()

				Db.Table(object.TableName()).Where("id = ?", object.ID).Updates(updates)
				//
				//history := models.History{}

				//Db.Table(history.TableName()).
				//	Where("gold_id = ? and offer = ? and price = ? and currency = ?", object.ID, item.Username, localPrice, item.DisplayCurrency).
				//	Find(&history)
				//
				//if history.ID < 1 {
				//	history.Offer = item.Username
				//	history.GoldId = object.ID
				//	history.Price = localPrice
				//	history.Currency = item.DisplayCurrency
				//	Db.Create(&history)
				//}

				//_, e := services.PubOfferTask(queue, item)
				//if e != nil {
				//	Log.Errorf("Task Pub Error: %s", e.Error())
				//	continue
				//}
			}
			Log.Infof(fomart, resp.Proxy.Name(), object.ID, serverName, item.Title, item.FormattedUnitPrice, item.OfferCurrency)
		}
	})
	c.Visit(url)
}
